﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Media.Imaging;
using System.IO;
using DaphneProfiBase;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using System.Reflection;
using GalaSoft.MvvmLight.Command;
using System.Windows.Controls;
using System.Windows;

using GameEngine;
using System.Windows.Media;
using GameEngine.PDNParser;
using System.Dynamic;

namespace DaphneControls.ViewModel
{
    /// <summary>
    /// View Model for PDN Game Library Control
    /// </summary>
    public class GameLibraryControlVM : ViewModelBase
    {
        /// <summary>
        /// Picture path and names
        /// </summary>
        private string _sPicturePath = "AppData\\Pictures";
        private string _sAddImage = "Add.png";
        private string _sDeleteImage = "Delete.png";

        /// <summary>
        /// Game Manager 
        /// </summary>
        private GameManager _gameManager = null;

        /// <summary>
        /// Raw game list from PDN parser
        /// </summary>
        private PDNRawGameList _pdnRawGameList = null;

        private List<GameRecord> _lstGameList = new List<GameRecord>();

        /// <summary>
        /// List of the mandatory PDN Tags
        /// </summary>
        private List<string> _lstPrimaryTags = new List<string>()
        {
            "Event","Site","Date","Round","White","Black","Result"
        };

        private List<string> _lstColumns = new List<string>();

        /// <summary>
        /// Initialization
        /// </summary>
        public GameLibraryControlVM()
        {
            // load images to bind
            string sAppPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            AddImage = new BitmapImage(new Uri(Path.Combine(sAppPath, _sPicturePath, _sAddImage)));
            DeleteImage = new BitmapImage(new Uri(Path.Combine(sAppPath, _sPicturePath, _sDeleteImage)));

            _lstColumns.AddRange(_lstPrimaryTags);

            // register message Loaded
            Messenger.Default.Register<NotificationMessage>(this, (nm) =>
                {
                    if (nm.Notification == Notify.PDNGameLibLoaded)
                    {
                        GenerateColumns();
                    }
                });

            // register message for gaining access to the game manager
            Messenger.Default.Register<NotificationMessage<GameManager>>(this, (nm) =>
                {
                    if (nm.Notification == Notify.SetGameManager)
                    {
                        _gameManager = nm.Content;
                    }
                });

            // register message for load PDN file
            Messenger.Default.Register<NotificationMessage<PDNRawGameList>>(this, (nm) =>
                {
                    if (nm.Notification == Notify.PDNFileLoaded)
                    {
                        _pdnRawGameList = nm.Content;
                        RefreshGames();
                    }
                });

            // create commands
            AddTag = new RelayCommand<Button>((btn) => { AddCommand(btn); });
            DeleteTag = new RelayCommand<object>((col) => { DeleteCommand(col); });
            OpenCommand = new RelayCommand(() =>
                {
                    OpenGame(SelectedGameRecord.Game);                    
                }, () =>
                {
                    return SelectedGameRecord != null;
                });
        }

        /// <summary>
        /// Add Button Image
        /// </summary>
        public BitmapSource AddImage
        { get; set; }
        
        /// <summary>
        /// Delete Image
        /// </summary>
        public BitmapSource DeleteImage
        { get; set; }

        /// <summary>
        /// Add Tag Column Command
        /// </summary>
        public RelayCommand<Button> AddTag
        { get; set; }

        /// <summary>
        /// Delete Tag Column Command
        /// </summary>
        public RelayCommand<object> DeleteTag
        { get; set; }

        public RelayCommand OpenCommand
        { get; set; }

        /// <summary>
        /// Loaded games
        /// </summary>
        public List<GameRecord> Games
        {
            get
            {
                return _lstGameList;
            }
        }

        /// <summary>
        /// Selected game record
        /// </summary>
        public GameRecord SelectedGameRecord
        { get; set; }

        /// <summary>
        /// Method for generating columns
        /// </summary>
        private void GenerateColumns()
        {
            // send message for generating columns
            Messenger.Default.Send<NotificationMessage<List<string>>>(
                new NotificationMessage<List<string>>(_lstColumns, Notify.PDNGameLibGenerateColumns));
        }

        /// <summary>
        /// Method for adding column tag
        /// </summary>
        private void AddCommand(Button btnAdd)
        {
            //// get position of the button
            //Point point = btnAdd.PointToScreen(new Point(0, 0));
            //PresentationSource source = PresentationSource.FromVisual(btnAdd);
            //System.Windows.Point targetPoints = source.CompositionTarget.TransformFromDevice.Transform(point);

            // get all PDN tags
            List<string> lstPDNTags = _gameManager.AllPDNTags;
            // filter out tags that all existing
            List<string> lstTags = lstPDNTags.Where(tag => _lstColumns.Contains(tag) == false).ToList();

            // create context menu
            ContextMenu menu = new ContextMenu();
            foreach (string tag in lstTags)
            {
                MenuItem menuItem = new MenuItem();
                menuItem.Header = tag;
                menuItem.Tag = tag;
                menuItem.Click += (s, e) =>
                    {
                        MenuItem mn = s as MenuItem;
                        AddColumn(mn.Tag as string);
                    };

                menu.Items.Add(menuItem);
            }

            menu.PlacementTarget = btnAdd;
            menu.IsOpen = true;
           
        }

        /// <summary>
        /// Deletes specific PDN Tag column
        /// </summary>
        private void DeleteCommand(object tag)
        {
            _lstColumns.Remove(tag as string);

            // send message to the code-behind to delete the column
            Messenger.Default.Send<NotificationMessage<string>>(
                new NotificationMessage<string>(tag as string, Notify.PDNGameLibDelColumn));                    
        }

        /// <summary>
        /// Adds the column tag
        /// </summary>
        private void AddColumn(string sTag)
        {
            // add to the collection
            _lstColumns.Add(sTag);

            // send message to code-behind to add column to the datagrid
            Messenger.Default.Send<NotificationMessage<string>>(
                new NotificationMessage<string>(sTag, Notify.PDNGameLibAddColumn));
        }

        /// <summary>
        /// Refreshes the games
        /// </summary>
        private void RefreshGames()
        {
            RaisePropertyChanging("Games");

            _lstGameList.Clear();

            // iterate through all games
            foreach (PDNRawGame game in _pdnRawGameList.Games) 
            {
                GameRecord rec = new GameRecord();

                // set all PDN tags
                foreach (var pdnTag in game.Headers)
                {
                    rec.AddTag(pdnTag.Key, pdnTag.Value);               
                }

                rec.Game = game;

                _lstGameList.Add(rec);
            }

            // bind it
            RaisePropertyChanged("Games");

            // refresh UI
            Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, Notify.InvalidateVisual));
        }

        /// <summary>
        /// Opens the game from game library
        /// </summary>
        private void OpenGame(PDNRawGame rawGame)
        {
            // iterpret game
            PDNInterpreter interpreter = new PDNInterpreter();
            GameManager gameManager = interpreter.InterpretGame(rawGame);

            // if successful
            if (gameManager != null)
            {
                // send message to the main window
                Messenger.Default.Send<NotificationMessage<GameManager>>
                    (new NotificationMessage<GameManager>(gameManager, Notify.OpenGameFromGameList));
            }
        }

        /// <summary>
        /// Game Record Data Grid Class
        /// </summary>
        public class GameRecord: DynamicObject
        {
            Dictionary<string, string> _tags = new Dictionary<string, string>();

            public PDNRawGame Game
            { get; set; }

            public void AddTag(string sTag, string sValue)
            {
                _tags[sTag] = sValue;
            }

            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                _tags[binder.Name] = value as string;
                return true;
            }

            /// <summary>
            /// Missing tags are represented as empty strings
            /// </summary>
            public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
            {
                string sResult;
                if (_tags.TryGetValue(binder.Name, out sResult) == true)
                {
                    result = sResult;
                }
                else
                {
                    result = string.Empty;
                }

                return true;
            }
        }
    }
}